#ifndef HASH_CPP
#define HASH_CPP
#include "Hash.h"

template <class Dominio, class Recorrido>
Hash<Dominio,Recorrido>::~Hash()
{
	vacio();
}

template <class Dominio, class Recorrido>
Hash<Dominio,Recorrido>::Hash(Puntero<FuncionHash<Dominio>> fh,Puntero<FuncionIguales<Dominio>> fi, int tamanio)
{
	this->fIguales=fi;
	this->fH=fh;
	arreglo = new celdaHash<Dominio,Recorrido>[tamanio];
	cantNodos = 0;
	cantMax = tamanio;
	for (int i= 0; i< tamanio; i++){
		arreglo [i].dato = NULL;
	}
}

template <class Dominio, class Recorrido>
void Hash<Dominio,Recorrido>::vacio()
{
	datoHash<Dominio,Recorrido>* borrar;
	for ( int i = 0;i < this->cantMax; i++ ){
		 while ( arreglo[i].dato != NULL ){
			 borrar = arreglo[i].dato;
				arreglo[i].dato = arreglo[i].dato->sig;
				delete borrar;
				borrar = NULL;
		}
	}
	this->fH=NULL;
	this->fIguales=NULL;
	cantNodos = 0;
}

template <class Dominio, class Recorrido>
Recorrido & Hash<Dominio,Recorrido>::insertar_Priv (datoHash<Dominio,Recorrido>* &dato, Dominio &d)
{
	if (dato == NULL){
		dato = new (datoHash<Dominio,Recorrido>);
		dato->dom = d;
		dato->sig = NULL;
		cantNodos++;
		return dato->rec;
    }else{ 
		if (this->fIguales->Iguales(dato->dom, d) == true){
			return dato->rec;
		}else{
			return insertar_Priv(dato->sig,d);
		}
	}
}

//template <class Dominio, class Recorrido>
//Puntero<Iterador<Dominio>> Hash<Dominio,Recorrido>::ObtenerIterador() const
//{
//	return new ArrayIterador<Dominio>(*this);
//}
template <class Dominio, class Recorrido>
Recorrido & Hash<Dominio,Recorrido>::operator[](Dominio &d)
{	
	nat pos = this->fH->CodigoDeHash (d);
	return this->insertar_Priv (this->arreglo[pos].dato, d);
}

template <class Dominio, class Recorrido>
bool Hash<Dominio,Recorrido>::estaDefinido(Dominio &d)
{
	int pos=this->fH->CodigoDeHash(d);
	datoHash<Dominio,Recorrido> * nodoAux = arreglo[pos].dato;
	while (nodoAux!= NULL){
		if ( this->fIguales->Iguales(nodoAux->dom, d) ){
			return true;
		}
		nodoAux = nodoAux->sig;
	}
	return false;
}

template <class Dominio, class Recorrido>
void Hash<Dominio,Recorrido>::eliminar(Dominio &d)
{
	datoHash<Dominio,Recorrido> * nodoAux = arreglo[this->fH->CodigoDeHash(d)].dato;
	datoHash<Dominio,Recorrido> * ant;
	while(nodoAux != NULL && nodoAux->dom != d){
		ant = nodoAux;
		nodoAux = nodoAux->sig;
	}
	if(nodoAux != NULL){
		if(nodoAux == arreglo[this->fH->CodigoDeHash(d)].dato){
			arreglo[this->fH->CodigoDeHash(d)].dato = arreglo[this->fH->CodigoDeHash(d)].dato->sig;
			delete nodoAux;
			this->cantNodos--;
		}
		else{
			ant->sig = nodoAux->sig;
			delete nodoAux;
			this->cantNodos--;
		}
	}
}

template <class Dominio, class Recorrido>
int Hash<Dominio,Recorrido>::tamanio()
{
	return cantMax;
}

template <class Dominio, class Recorrido>
int Hash<Dominio,Recorrido>::size()
{
	return cantNodos;
}
template <class Dominio, class Recorrido>
Puntero<Iterador<Puntero<Tupla<Dominio,Recorrido>>>>  Hash<Dominio,Recorrido>::ObtenerIterador() const 
{
	ListaImp<Puntero<Tupla<Dominio,Recorrido>>> *lista=new ListaImp<Puntero<Tupla<Dominio,Recorrido>>>();
	datoHash<Dominio, Recorrido>* aux;
	for(int i = 0; i < this->cantMax; i++){
	aux=this->arreglo[i].dato;
		while(aux != NULL){
		Puntero<Tupla<Dominio,Recorrido>> l=new Tupla<Dominio,Recorrido>(aux->dom,aux->rec);
			lista->Agregar(l);
			aux = aux->sig;
		}
	}
	Puntero<Iterador<Puntero<Tupla<Dominio,Recorrido>>>> it=new IteradorListaImp<Puntero<Tupla<Dominio,Recorrido>>>(*lista);
	delete aux;
	delete lista;
	return it;
}
#endif
